feat(sdk): project-scope session-start hook install, status, and uninstall - #140
Merged
kunchenguid merged 4 commits intoAug 19, 2026
Merged
Conversation
Add scope?: "user" | "project" (default "user", zero behavior change) and projectDir?: string to installSessionStartHooks, plus two new counterpart functions: - sessionStartHookStatus(): read-only status per agent/scope, including the shared Codex user-level [features].hooks flag. - uninstallSessionStartHooks(): removes only marker-matched managed entries at the requested scope. Project scope targets: - Claude Code: <projectDir>/.claude/settings.json (native project scope) - Codex hooks: <projectDir>/.codex/hooks.json, while the [features].hooks = true flag is always ensured in the USER-level config.toml, since repo-level Codex hooks still require it there - OpenCode: <projectDir>/.opencode/plugins/, symmetric with the documented global ~/.config/opencode/plugins/ convention (https://opencode.ai/docs/plugins/) Introduces computeSessionStartHookRemoval() as the removal counterpart to computeSessionStartHookUpdate(), and a resolveHookScopeTargets() helper shared by install/status/uninstall so scope-path resolution lives in one place. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Document scope/projectDir on installSessionStartHooks, the new sessionStartHookStatus()/uninstallSessionStartHooks() exports, the per-agent user-vs-project path table, and the Codex user-level feature-flag interaction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
This would close #45 |
Owner
|
Speaking as Kun's firstmate: reviewed the diff. This is opt-in new SDK behavior ( Approving and squash-merging. |
kunchenguid
approved these changes
Aug 19, 2026
kunchenguid
left a comment
Owner
There was a problem hiding this comment.
Speaking as Kun's firstmate: approving. Opt-in project-scope hooks; default user/home unchanged; CI green.
Owner
|
Speaking as Kun's firstmate: this is merged. Thank you @themightychris — really appreciate you taking the time on this. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Add opt-in project-scope support to axi-sdk-js's session-hook machinery so -axi tools can offer 'setup hooks --scope project': scope ('user'|'project', default user — zero behavior change for existing callers) and projectDir options on installSessionStartHooks, plus NEW sessionStartHookStatus() and uninstallSessionStartHooks() counterparts (none existed before — every tool hand-rolls status/uninstall today; these are new API surface designed in this change, reusing the existing marker-inference and JSON-patch machinery). Project scope: /.claude/settings.json for Claude Code (native), /.codex/hooks.json for Codex with the [features].hooks flag still ensured in the USER config.toml (repo hooks require the user-level flag), /.opencode/plugins/ for OpenCode (documented project-plugin dir per opencode.ai/docs/plugins). A resolveHookScopeTargets() helper centralizes scope→path resolution so install/status/uninstall can't drift. 15 new tests (49 in hooks.test.ts, 168 total in package); format/lint/build/test all clean. Motivated by a Jarvus tool survey: only specops supports project-scoped ambient hooks today, hand-rolled; landing this in the SDK gives every -axi tool the capability in one place.
What Changed
installSessionStartHooks()gains opt-inscope: "user" | "project"(default"user", unchanged behavior for existing callers) andprojectDiroptions. Project scope targets<repo>/.claude/settings.jsonfor Claude Code,<repo>/.codex/hooks.jsonfor Codex (while still ensuring the[features].hooksflag in the user-levelconfig.toml), and<repo>/.opencode/plugins/for OpenCode.sessionStartHookStatus()anduninstallSessionStartHooks()APIs, backed by a newcomputeSessionStartHookRemoval()JSON-patch helper and a sharedresolveHookScopeTargets()helper that centralizes scope→path resolution across install, status, and uninstall.Risk Assessment
✅ Low: The fix round correctly scopes SessionStart removal with a per-section flag, adds a genuine fail-before/pass-after regression test, and introduces no new behavior beyond the prescribed fix; all remaining prior findings were informational and explicitly acknowledged by the user.
Testing
Ran the targeted hooks test file (50/50 passing, covering the new scope/status/uninstall API and the default-user-scope backward-compatibility guarantee), then built the package and ran an end-to-end demo driving the public API from the built dist exactly as a tool's
setup hooks --scope projectwould: the transcript shows project-scoped Claude/Codex/OpenCode artifacts created under the repo, the Codex feature flag correctly ensured in the user-level config.toml, scope-accurate status reporting, and marker-scoped uninstall that preserves unrelated hooks and the shared user flag. All intent constraints demonstrated; no issues found.Evidence: Project-scope hooks lifecycle demo transcript (install → status → uninstall)
Evidence: Demo driver script (imports built dist as a -axi tool would)
Evidence: Key transcript excerpt: project-scope install results
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 3 issues found → auto-fixed ✅
packages/axi-sdk-js/src/hooks.ts:238- computeSessionStartHookRemoval shares onechangedflag between the legacysession_startblock and theSessionStartblock. If a managed legacy entry is removed whilehooks.SessionStartis an empty array (e.g.{hooks: {session_start: [managed], SessionStart: []}}), theif (changed)branch at line 238 deletes the untouchedSessionStart: []key (or re-assigns unchanged groups). The effect is semantically inert for Claude/Codex, but a per-section flag would keep the removal strictly scoped to marker-matched entries.packages/axi-sdk-js/src/hooks.ts:883- uninstallSessionStartHooks() returns silently (without invoking onError) when the marker cannot be inferred and none is passed, so an explicit uninstall invoked from a non-inferable context reports nothing while removing nothing. The README documents this as a deliberate mirror of install's permissive default and a test pins it, so this is an acknowledged tradeoff rather than a defect; if tools ever surface confusing 'uninstalled but still active' reports, routing this case through onError would be the follow-up.packages/axi-sdk-js/test/hooks.test.ts:843- Intent conformance: the user intent states "15 new tests (49 in hooks.test.ts, 168 total in package)". The 49-test count in hooks.test.ts matches, but the file grew from 37 to 49 test cases with no parameterized (it.each) expansion and no other test file changed in this range, so the change adds 12 new tests, not 15. Every functional acceptance criterion (scope default, project paths, user-level Codex flag, status, uninstall, marker inference edge cases) has test coverage, so this appears to be a narrative miscount rather than missing coverage — surfacing per the conformance rule instead of resolving it.🔧 Fix: scope SessionStart removal with per-section changed flag
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
npx vitest run test/hooks.test.tsin packages/axi-sdk-js — 50/50 pass, including the 7-testsession hook scope (user vs project)suite exercising install/status/uninstall against real temp filesystemsnpm run buildin packages/axi-sdk-js — TypeScript build of the new API surface succeedsEnd-to-end lifecycle demo (node demo-project-scope.mjs) importing the built dist/index.js as a-axitool would: installSessionStartHooks({scope:'project'}) → sessionStartHookStatus at both scopes → uninstallSessionStartHooks({scope:'project'}), with a fake $HOME and repo dir seeded with pre-existing user settings and an unrelated project hookVerified in the demo transcript: project install writes <repo>/.claude/settings.json, <repo>/.codex/hooks.json, <repo>/.opencode/plugins/axi-gh-axi.js; Codex [features].hooks=true lands in USER config.toml; user-scope files untouched; unrelated project hook preserved through install and uninstall; uninstall removes only marker-matched entries and leaves the shared user flagConfirmed worktree clean after removing transient dist/ build output✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.